const OperateTypes = {
    ERASE: 0,
    RECOVER: 1,
  };
  const ScaleChangeType = {
    ZOOMIN: 0, // 放大
    ZOOMOUT: 1, // 缩小
  };
  const HistoryTypes = {
    EDIT: 0,
    SCALE: 1,
  };
  const editItem = {
    originUrl: "./imgs/1.jpg",
    finalUrl: "./imgs/1.png",
  };
  const tool = {
    init: function () {
      this.initVariables();
      this.setCanvasCtx();
      this.setCursor();
      this.setScaleNum();
      this.events();
    },
    initVariables: function () {
      this.eraseSlider = document.getElementById("eraseSlider");
      this.restoreSlider = document.getElementById("restoreSlider");
      this.canvasWrapper = document.getElementById("canvasWrapper");
      this.canvas = document.getElementById("canvas");
      this.ctx = this.canvas.getContext("2d");
      this.cursor = document.getElementById("cursor");
      this.backward = document.getElementById("backward");
      this.forward = document.getElementById("forward");
      this.scaleWrapper = document.getElementById("scaleWrapper");
      this.zoomInBtn = document.getElementById("zoomIn");
      this.zoomOutBtn = document.getElementById("zoomOut");
      this.tabHeader = document.getElementById("tabHeader");
      this.tabContent = document.getElementById("tabContent");
      this.dragging = false;
      this.offscreenCanvasOri = null;
      this.offscreenContextOri = null;
      this.offscreenCanvasEdit = null;
      this.offscreenContextEdit = null;
      this.imageOri = new Image();
      this.imageFinal = new Image();
      
      this.imgXY = {
        x: 0,
        y: 0,
      };
      this.maxHistoryStep = 30;
      this.currentXY = {
        x: 0,
        y: 0,
      };
    this.mouseDown = {
      x: 0,
      y: 0,
    };
    this.lastXY = {
      x: 0,
      y: 0,
    };
      this.operateList = [
        {
          type: OperateTypes.ERASE,
          text: "擦除",
        },
        {
          type: "restore",
          text: "恢复",
        },
      ];
      this.eraseBrushSize = 24;
      this.restoreBrushSize = 24;
     
      this.scale = 1;
      this.history = {
        step: -1,
        operations: [],
      };
      this.dragging = false;
      this.isOver = false;
      this.activeOperate = OperateTypes.ERASE;
    },
    setScaleNum: function () {
      this.scaleWrapper.innerHTML = parseInt(this.scale * 100) + "%";
    },
    setCursor: function () {
      const brushSize =
        this.activeOperate === OperateTypes.ERASE
          ? this.eraseBrushSize
          : this.restoreBrushSize;
      this.cursor.style = `display : ${this.isOver ? "block" : "none"};
      width:${brushSize}px;
      height: ${brushSize}px; 
      border-radius: ${brushSize / 2}px;
      left: ${this.lastXY.x - brushSize / 2}px;
      top: ${this.lastXY.y - brushSize / 2}px;`;
    },
    events: function () {
      this.eraseSlider.onchange = () => {
        this.eraseBrushSize = this.eraseSlider.value;
        this.setCursor();
      };
  
      this.restoreSlider.onchange = () => {
        this.restoreBrushSize = this.restoreSlider.value;
        this.setCursor();
      };
  
      this.canvasWrapper.onmouseenter = () => {
        this.canvasMouseEnter();
      };
      this.canvasWrapper.onmouseleave = () => {
        this.canvasMouseLeave();
      };
  
      this.canvasWrapper.onmousedown = (e) => {
        this.canvasMouseDown(e);
      };
      this.canvasWrapper.onmousemove = (e) => {
        this.canvasMouseMove(e);
      };
      this.canvasWrapper.onmouseup = (e) => {
        this.canvasMouseUp(e);
      };
  
      document.onmouseup = () => {
        this.dragging = false;
        console.log("document mouse up");
      };
  
      // 历史记录
  
      this.backward.onclick = () => {
        this.stepBackward();
      };
      this.forward.onclick = () => {
        this.stepForward();
      };
  
      // 放大缩小
      this.zoomInBtn.onclick = () => {
        this.zoomOutBtn.className = "";
        if (this.scale >= 4) {
          this.zoomInBtn.className = "disabled";
          return false;
        }
        this.changeScale(ScaleChangeType.ZOOMIN);
        this.zoomInBtn.className = "";
      };
  
      this.zoomOutBtn.onclick = () => {
        this.zoomInBtn.className = "";
        if (this.scale <= 0.2) {
          this.zoomOutBtn.className = "disabled";
          return false;
        }
        this.zoomOutBtn.className = "";
        this.changeScale(ScaleChangeType.ZOOMOUT);
      };
  
      // tab切换
      const lis = this.tabHeader.getElementsByTagName("li");
      const contents = this.tabContent.getElementsByClassName("tab-content");
      const that = this;
      for (var i = 0; i < lis.length; i++) {
        const current = i;
        lis[i].onclick = function (e) {
          for (var k = 0; k < lis.length; k++) {
            lis[k].className = "";
          }
          e.target.className = "active";
          that.activeOperate = current;
          for (var j = 0; j < contents.length; j++) {
            contents[j].style = "display:none;";
          }
          contents[current].style = "display: block";
          that.setCursor();
        };
      }
    },
  
    createOffscreenCanvas: function (offscreenCanvas, offscreenContext, cb) {
      offscreenCanvas = document.createElement("canvas");
      document.getElementById("leftWrapper").appendChild(offscreenCanvas);
  
      if (canvas) {
        offscreenCanvas.width = canvas.width;
        offscreenCanvas.height = canvas.height;
      }
  
      offscreenContext = offscreenCanvas.getContext("2d");
      return {
        offscreenCanvas,
        offscreenContext,
      };
    },
    setCanvasCtx: function () {
      const offscreenOri = this.createOffscreenCanvas(
        this.offscreenCanvasOri,
        this.offscreenContextOri
      );
      this.offscreenCanvasOri = offscreenOri.offscreenCanvas;
      this.offscreenContextOri = offscreenOri.offscreenContext;
  
      this.setOffCanvasImage(
        this.offscreenCanvasOri,
        this.offscreenContextOri,
        this.imageOri,
        editItem.originUrl
      );
  
      const offscreenEdit = this.createOffscreenCanvas(
        this.offscreenCanvasEdit,
        this.offscreenContextEdit
      );
      this.offscreenCanvasEdit = offscreenEdit.offscreenCanvas;
      this.offscreenContextEdit = offscreenEdit.offscreenContext;
  
      this.setOffCanvasImage(
        this.offscreenCanvasEdit,
        this.offscreenContextEdit,
        this.imageFinal,
        editItem.finalUrl,
        () => {
          this.setCanvasImage();
        }
      );
    },
    addHistory: function (canvas, ctx, type) {
      console.log("addHistory");
  
      // 增加一条历史记录将此条后面的所有历史记录删除
      this.history.operations.length = this.history.step + 1;
      if (this.history.operations.length + 1 > this.maxHistoryStep) {
        this.history.operations.shift();
        this.history.step = this.history.step - 1;
      }
      const { scale } = this;
      const newImageData = this.offscreenContextEdit.getImageData(
        0,
        0,
        canvas.width,
        canvas.height
      );
      this.history.operations.push({
        type,
        scale,
        offscreenEditImageData: new ImageData(
          newImageData.data,
          newImageData.width,
          newImageData.height
        ),
      });
      this.history.step = this.history.step + 1;
  
      this.isHistoryBtnDisabled();
    },
  
    stepBackward: function () {
      // 向后一条
      if (this.history.step + 1 >= this.history.operations.length) {
        return false;
      }
  
      this.history.step += 1;
      this.isHistoryBtnDisabled();
      this.setHistoryData();
    },
    isHistoryBtnDisabled: function () {
      if (this.history.step - 1 < 0) {
        this.forward.className = "disabled";
      } else {
        this.forward.className = "";
      }
      if (this.history.step + 1 >= this.history.operations.length) {
        this.backward.className = "disabled";
      } else {
        this.backward.className = "";
      }
    },
    stepForward: function () {
      // 向前一条
      if (this.history.step - 1 < 0) {
        return false;
      }
  
      this.history.step -= 1;
      this.isHistoryBtnDisabled();
      this.setHistoryData();
    },
    setHistoryData: function () {
      /**
       * 1. 修改画面, 需要同步offscreenCanvasEdit
       * 2. 放大缩小，需要同步offscreenCanvasOri
       */
      const { history: newHistory } = this;
  
      const { type, scale, offscreenEditImageData } = newHistory.operations[
        newHistory.step
      ];
      const { scale: oldScale } = this;
      if (
        this.offscreenContextEdit &&
        this.canvas &&
        this.ctx &&
        this.offscreenContextOri &&
        this.offscreenCanvasEdit
      ) {
        const w = canvas.width,
          h = canvas.height;
        const sw = scale * w,
          sh = scale * h;
  
        this.imgXY = {
          x: w / 2 - sw / 2,
          y: h / 2 - sh / 2,
        };
        this.offscreenContextEdit.putImageData(offscreenEditImageData, 0, 0);
        if (oldScale !== scale) {
          this.offscreenContextOri.clearRect(0, 0, w, h);
          this.offscreenContextOri.drawImage(
            this.imageOri,
            this.imgXY.x,
            this.imgXY.y,
            sw,
            sh
          );
        }
  
        this.ctx.clearRect(0, 0, w, h);
        this.ctx.drawImage(
          this.offscreenCanvasEdit,
          this.imgXY.x,
          this.imgXY.y,
          sw,
          sh
        );
        this.scale = scale;
        this.setScaleNum();
      }
    },
    setCanvasImage: function () {
      this.ctx.drawImage(
        this.imageFinal,
        0,
        0,
        this.canvas.width,
        this.canvas.height
      );
      this.addHistory(this.canvas, this.ctx, HistoryTypes.EDIT);
    },
  
    setOffCanvasImage: function (
      offscreenCanvas,
      offscreenContext,
      image,
      url,
      cb
    ) {
      image.src = url;
      console.log(offscreenCanvas);
      image.onload = function () {
        if (offscreenContext && offscreenCanvas) {
          offscreenContext.drawImage(
            image,
            0,
            0,
            offscreenCanvas.width,
            offscreenCanvas.height
          );
        }
  
        cb ? cb() : null;
      };
    },
  
    handleChange: function (value) {
      const size = value;
      this.activeOperate === OperateTypes.ERASE
        ? (this.eraseBrushSize = size)
        : (this.restoreBrushSize = size);
  
      this.setCursor();
    },
    changeTab: function (value) {
      this.activeOperate = value;
      this.setCursor();
    },
    windowToCanvas: function (x, y) {
      const canvasBox = canvas
        ? canvas.getBoundingClientRect()
        : { left: 0, top: 0 };
      return {
        x: x - canvasBox.left,
        y: y - canvasBox.top,
      };
    },
    canvasMouseDown: function (e) {
      console.log("canvasMouseDown");
      const loc = this.windowToCanvas(e.clientX, e.clientY);
      this.mouseDown = JSON.parse(JSON.stringify(loc));
      this.lastXY = JSON.parse(JSON.stringify(loc));
      this.dragging = true;
      this.setCursor();
      
    },
    canvasMouseMove: function (e) {
      console.log("canvasMouseMove");
  
      if (!canvas) {
        return false;
      }
      const { dragging, isOver } = this;
      const loc = this.windowToCanvas(e.clientX, e.clientY);
      const isInCanvas =
        loc.x > 0 && loc.x < canvas.width && loc.y > 0 && loc.y < canvas.height;
    isInCanvas ? (this.currentXY = JSON.parse(JSON.stringify(loc))) : null;
      
      if (dragging) {
          this.eraseRestoreLast();
        }
        if (isInCanvas) {
          this.lastXY = JSON.parse(JSON.stringify(loc));
          this.setCursor();
        }
    },
    canvasMouseUp: function (e) {
      console.log("canvasMouseUp");
      const { dragging } = this;
      if (dragging) {
        this.eraseRestoreLast();
        this.addHistory(this.canvas, this.ctx, HistoryTypes.EDIT);
      }
    },
    canvasMouseEnter: function () {
      console.log("canvasMouseEnter");
      this.isOver = true;
      this.setCursor();
    },
    canvasMouseLeave: function () {
      console.log("canvasMouseLeave");
      this.isOver = false;
      this.setCursor();
    },
    eraseRestoreLast: function () {
      const { activeOperate } = this;
      if (activeOperate === OperateTypes.ERASE) {
        this.eraseCircle(this.canvas, this.ctx, false);
        this.eraseRectangle(this.canvas, this.ctx, false);
        this.eraseCircle(
          this.offscreenCanvasEdit,
          this.offscreenContextEdit,
          true
        );
        // 移动快的时候可能两个点之间有距离，在两个点之间画一个四边形，四边形的两条边是圆的切线，用以清除两个点之间的图像
        this.eraseRectangle(
          this.offscreenCanvasEdit,
          this.offscreenContextEdit,
          true
        );
      } else {
        this.restoreCircle(this.canvas, this.ctx, false);
        this.restoreRectangle(this.canvas, this.ctx, false);
        this.restoreCircle(
          this.offscreenCanvasEdit,
          this.offscreenContextEdit,
          true
        );
        this.restoreRectangle(
          this.offscreenCanvasEdit,
          this.offscreenContextEdit,
          true
        );
      }
    },
    eraseCircle: function (canvas, ctx, isOffscreen) {
      const { eraseBrushSize, scale } = this;
      ctx.save();
      if (isOffscreen) {
        // 变换坐标系
        // ctx.arc(
        //   (this..x - this.imgXY.x) / scale,
        //   (this.currentXY.y - this.imgXY.y) / scale,
        //   eraseBrushSize / scale / 2,
        //   0,
        //   2 * Math.PI,
        //   false
        // );
        ctx.translate(-this.imgXY.x/scale,-this.imgXY.y/scale);
        ctx.scale(1/scale,1/scale)
        
      } 
      
      ctx.beginPath();
      ctx.arc(
        this.lastXY.x,
        this.lastXY.y,
        eraseBrushSize / 2,
        0,
        2 * Math.PI,
        false
      );

      ctx.clip();
      // 擦除
      ctx.clearRect(0, 0, canvas.width, canvas.height);
  
      ctx.restore();
    },
    getRectangle: function (x1, y1, x2, y2, r) {
      /**
       *  移动快的时候可能两个点之间有距离，在两个点之间画一个四边形，四边形的两条边是圆的切线，用以清除两个点之间的图像
       * (x1,y1) (x1,y1) 两个圆的圆心,r是半径，两个圆半径相等
       * 需要返回四边形的四个顶点，存在points中
       */
      const points = [];
      const angle = Math.atan((x2 - x1) / (y2 - y1));
  
      points[0] = {
        x: x1 + r * Math.cos(angle),
        y: y1 - r * Math.sin(angle),
      };
      points[1] = {
        x: x2 + r * Math.cos(angle),
        y: y2 - r * Math.sin(angle),
      };
      points[2] = {
        x: x2 - r * Math.cos(angle),
        y: y2 + r * Math.sin(angle),
      };
      points[3] = {
        x: x1 - r * Math.cos(angle),
        y: y1 + r * Math.sin(angle),
      };
      return points;
    },
    eraseRectangle: function (canvas, ctx, isOffscreen) {
      let points = [];
  
      ctx.save();
      
      if (isOffscreen) {
        // points = this.getRectangle(
        //   (this.lastXY.x - this.imgXY.x) / this.scale,
        //   (this.lastXY.y - this.imgXY.y) / this.scale,
        //   (this.currentXY.x - this.imgXY.x) / this.scale,
        //   (this.currentXY.y - this.imgXY.x) / this.scale,
        //   this.eraseBrushSize / this.scale / 2
        // );
        ctx.translate(-this.imgXY.x/this.scale,-this.imgXY.y/this.scale);
        ctx.scale(1/this.scale,1/this.scale);
      } 
      points = this.getRectangle(
        this.lastXY.x,
        this.lastXY.y,
        this.currentXY.x,
        this.currentXY.y,
        this.eraseBrushSize / 2
      );
      ctx.beginPath();
      this.drawRectangle(points, ctx);
      ctx.clip();
      // 擦除
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.restore();
    },
    drawRectangle: function (points, ctx) {
      ctx.moveTo(points[0].x, points[0].y);
      for (let i = 1; i < points.length; i++) {
        ctx.lineTo(points[i].x, points[i].y);
      }
      ctx.closePath();
      ctx.strokeStyle = "red";
      ctx.lineWidth = 1;
      ctx.stroke();
    },
    restoreRectangle: function (canvas, ctx, isOffscreen) {
      const { restoreBrushSize, scale } = this;
  
      if (this.canvas && this.ctx) {
        ctx.save();
        ctx.beginPath();
        let points = [];
        if (isOffscreen) {
          ctx.translate(-this.imgXY.x/this.scale,-this.imgXY.y/this.scale);
          ctx.scale(1/this.scale,1/this.scale);
          // points = this.getRectangle(
          //   (this.lastXY.x - this.imgXY.x) / scale,
          //   (this.lastXY.y - this.imgXY.y) / scale,
          //   (this.currentXY.x - this.imgXY.x) / scale,
          //   (this.currentXY.y - this.imgXY.x) / scale,
          //   restoreBrushSize / scale / 2
          // );
          // this.drawRectangle(points, ctx);
  
          // ctx.clip();
          // // 从离屏幕canvas复制
          // ctx.drawImage(this.imageOri, 0, 0, canvas.width, canvas.height);
        } 
          points = this.getRectangle(
            this.lastXY.x,
            this.lastXY.y,
            this.currentXY.x,
            this.currentXY.y,
            restoreBrushSize / 2
          );
          this.drawRectangle(points, ctx);
          ctx.clip();
          
            // 从离屏幕canvas复制
            this.offscreenCanvasOri
            ? ctx.drawImage(this.offscreenCanvasOri, 0, 0)
            : null;
         
        ctx.restore();
      }
    },
  
    restoreCircle: function (canvas, ctx, isOffscreen) {
      if (this.canvas && this.ctx) {
        const { restoreBrushSize, scale } = this;
  
        ctx.save();
        ctx.beginPath();
  
        if (isOffscreen) {
          ctx.translate(-this.imgXY.x/this.scale,-this.imgXY.y/this.scale);
          ctx.scale(1/this.scale,1/this.scale);
        } 

        ctx.arc(
          this.lastXY.x,
          this.lastXY.y,
          restoreBrushSize / 2,
          0,
          2 * Math.PI,
          false
        );
        ctx.clip();
       
          // 从离屏幕canvas复制
          this.offscreenCanvasOri
          ? ctx.drawImage(this.offscreenCanvasOri, 0, 0)
          : null;
       
        ctx.restore(); 
      }
    },
  
    changeScale: function (type) {
      const { scale } = this;
      if (type === ScaleChangeType.ZOOMIN) {
        this.scale = Number((scale + 0.2).toFixed(1));
      } else {
        this.scale = Number((scale - 0.2).toFixed(1));
      }
      this.setScaleNum();
      this.drawScaleImage();
    },
    drawScaleImage: function () {
      const { scale } = this;
  
      if (this.canvas && this.ctx) {
        const w = canvas.width,
          h = canvas.height;
        const sw = scale * w,
          sh = scale * h;
        this.imgXY = {
          x: w / 2 - sw / 2,
          y: h / 2 - sh / 2,
        };
        // 用于记录原图的离屏canvas也需要对应放大缩小
        if (this.offscreenContextOri && this.offscreenCanvasOri) {
          this.offscreenContextOri.clearRect(
            0,
            0,
            this.canvas.width,
            this.canvas.height
          );
          this.offscreenContextOri.drawImage(
            this.imageOri,
            this.imgXY.x,
            this.imgXY.y,
            sw,
            sh
          );
        }

        // 通过变换坐标系来实现放大缩小
        // 把坐标系的原点变换到图片的左顶点
        this.ctx.save();
        this.ctx.clearRect(0,0,w,h)
        this.ctx.translate(this.imgXY.x,this.imgXY.y);
        this.ctx.scale(scale,scale);
        
        
        this.ctx.drawImage(this.offscreenCanvasEdit,0,0,w,h)
        this.ctx.restore();
        // 通过计算drawImage的参数来实现放大缩小
        // if (this.offscreenCanvasEdit) {
        //   this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        //   this.ctx.drawImage(
        //     this.offscreenCanvasEdit,
        //     this.imgXY.x,
        //     this.imgXY.y,
        //     sw,
        //     sh
        //   );
        // }
        this.addHistory(this.canvas, this.ctx, HistoryTypes.SCALE);
      }
    },
  };
  window.onload = function () {
    tool.init();
  };
  